#include "Operation.h"
#include "Operand.h"

Operation::Operation(std::string name, unsigned int priority, Node * parent)
{
	this->_name = name;
	this->_priority = priority;
	this->_parent = parent;
}

DataType Operation::getDataType() const
{
	return _dataType;
}

void Operation::setDataType(DataType dataType)
{
	_dataType = dataType;
}

void Operation::print(std::string& tree)
{
	tree.append(this->_name);
	for (unsigned int i=0; i < this->countChildren(); i++)
	{
		tree.append("(");
		this->getChild(i)->print(tree);
		tree.append(")");
	}
}

void Operation::clone(Node** node)
{
	(*node) = new Operation(this->_name, this->_priority, this->_parent);
	if (this->countChildren() > 0)
	{
		for (unsigned int i=0; i < this->countChildren(); i++)
		{
			Node* ch;
			this->getChild(i)->clone(&ch);
			(*node)->addChild(ch);
		}
	}
}

Operation::~Operation(void)
{
}

int Operation::getType() const
{
	return OPERATION;
}

bool Operation::checkNode(std::vector<Variables> & vars, const Node* goal)
{
	std::string first, second;

	if (this->_name.compare("+") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		return true;
	}
	else if (this->_name.compare("-") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		return true;
	}
	else if (this->_name.compare("*") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		return true;
	}
	else if (this->_name.compare("/") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		return true;
	}
	else if (this->_name.compare("mod") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		return true;
	}
	else if (this->_name.compare("div") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		return true;
	}
	else if (this->_name.compare(">") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		first = this->getChild(0)->getValue(vars);
		second = this->getChild(1)->getValue(vars);
		return atof(first.data()) > atof(second.data());
	}
	else if (this->_name.compare("<") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		first = this->getChild(0)->getValue(vars);
		second = this->getChild(1)->getValue(vars);
		return atof(first.data()) < atof(second.data());
	}
	else if (this->_name.compare("=") == 0)
	{
		if (this->getChild(0)->getType() == OPERAND && !((Operand*)(this->getChild(0)))->isConst())
		{
			for(int i=0;i<vars.size();i++)
			{
				if(vars[i].getName().compare(this->getChild(0)->getName()) == 0 && vars[i].getValue().compare("_") == 0)
				{
					vars[i].setValue(this->getChild(1)->getValue(vars));
					return true;
				}
			}
		}
		return this->getChild(0)->getValue(vars).compare(this->getChild(1)->getValue(vars)) == 0;
	}
	else if (this->_name.compare("<>") == 0)
	{
		return this->getChild(0)->getValue(vars).compare(this->getChild(1)->getValue(vars)) == 0;
	}
	else if (this->_name.compare("=<") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		first = this->getChild(0)->getValue(vars);
		second = this->getChild(1)->getValue(vars);
		return atof(first.data()) <= atof(second.data());
	}
	else if (this->_name.compare(">=") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		first = this->getChild(0)->getValue(vars);
		second = this->getChild(1)->getValue(vars);
		return atof(first.data()) >= atof(second.data());
	}
	return false;
}

std::string Operation::getValue(std::vector<Variables> vars)
{
	double first, second, result;
	
	first = atof(this->getChild(0)->getValue(vars).data());
	second = atof(this->getChild(1)->getValue(vars).data());
	if (this->_name.compare("+") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		result = first + second;
	}
	else if (this->_name.compare("-") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		result = first - second;
	}
	else if (this->_name.compare("*") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		result = first * second;
	}
	else if (this->_name.compare("/") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		result = first / second;
	}
	else if (this->_name.compare("mod") == 0 && (this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		result = (int)first % (int)second;
	}
	else if (this->_name.compare("div") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		result = (int)first / (int)second;
	}
	else if (this->_name.compare(">") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		if (first > second)
			return "true";
		else
			return "false";
	}
	else if (this->_name.compare("<") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		if (first < second)
			return "true";
		else
			return "false";
	}
	else if (this->_name.compare("=") == 0)
	{
		if (first == second)
			return "true";
		else
			return "false";
	}
	else if (this->_name.compare("<>") == 0)
	{
		if (first != second)
			return "true";
		else
			return "false";
	}
	else if (this->_name.compare("=<") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{		
		if (first <= second)
			return "true";
		else
			return "false";
	}
	else if (this->_name.compare(">=") == 0 && (this->_dataType == REAL || this->_dataType == INTEGER || this->_dataType == UNKNOWN))
	{
		if (first >= second)
			return "true";
		else
			return "false";
	}
	char szbuff[255]; 
	sprintf(szbuff, "%g", result);

	return szbuff;
}